from kivy.graphics import Color, Canvas
from kivy.graphics.vertex_instructions import Rectangle
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider
from kivy.uix.widget import Widget
from kivy.properties import (BoundedNumericProperty,
                             VariableListProperty,
                             ReferenceListProperty)
from colorsys import rgb_to_hsv, hsv_to_rgb

class _ColorSliders(GridLayout):

    _r = BoundedNumericProperty(0.0, min=0.0, max=1.0)
    _g = BoundedNumericProperty(0.0, min=0.0, max=1.0)
    _b = BoundedNumericProperty(0.0, min=0.0, max=1.0)
    _a = BoundedNumericProperty(1.0, min=0.0, max=1.0)
    rgba_color = ReferenceListProperty(_r, _g, _b, _a)

    def __init__(self, **kwargs):
        GridLayout.__init__(self, **kwargs)
        self.cols = 2
        self.padding = 10
        self._h = 1.0
        self._s = 1.0
        self._v = 1.0
        self._updating_color = False
        # determine label width using widest text
        slabel = Label(text='Saturation')
        slabel.texture_update()
        lwidth = slabel.texture_size[0] * 1.5
        lheight = slabel.height
        # hue
        label = Label(text='Hue',
                      size_hint_x=None,
                      width=lwidth)
        self._h_slider = Slider(min=0.0, max=1.0, step=0.01, value=self._h)
        self._h_slider.bind(value=self.w_handle_on_hue_slider_value)
        self.add_widget(label)
        self.add_widget(self._h_slider)
        # saturation
        slabel.size_hint_x=None
        slabel.width=lwidth
        self._s_slider = Slider(min=0.0, max=1.0, step=0.01, value=self._s)
        self._s_slider.bind(value=self.w_handle_on_saturation_slider_value)
        self.add_widget(slabel)
        self.add_widget(self._s_slider)
        # value
        label = Label(text='Value',
                      size_hint_x=None,
                      width=lwidth)
        self._v_slider = Slider(min=0.0, max=1.0, step=0.01, value=self._h)
        self._v_slider.bind(value=self.w_handle_on_value_slider_value)
        self.add_widget(label)
        self.add_widget(self._v_slider)
        
    @property
    def color(self):
        return list(self.rgba_color)

    @color.setter
    def color(self, value):
        """value is a list [1.0, 1.0, 1.0, 1.0]
        """
        if isinstance(value, list) and len(value) == 4:
            self._a = value[3]
            self._h_slider.value, self._s_slider.value, self._v_slider.value\
                = rgb_to_hsv(value[0], value[1], value[2])

    def w_handle_on_hue_slider_value(self, instance, value):
        self._h = value
        self._hsv_to_color()

    def w_handle_on_saturation_slider_value(self, instance, value):
        self._s = value
        self._hsv_to_color()

    def w_handle_on_value_slider_value(self, instance, value):
        self._v = value
        self._hsv_to_color()
        
    # private methods

    def _hsv_to_color(self):
        r, g, b = hsv_to_rgb(self._h, self._s, self._v)
        self.rgba_color = [r, g, b, self._a]


class _ColorDisplay(Widget):

    fg_color = VariableListProperty([1.0, 0.0, 0.0, 1.0], length=4)
    bg_color = VariableListProperty([1.0, 1.0, 1.0, 1.0], length=4)

    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
        self.canvas = Canvas()
        self._is_visible = False
        self._is_drawing = False
        self._bg_rectangle = Rectangle()
        self._fg_rectangle = Rectangle()
        self.bind(size=self.handle_on_size,
                  pos=self.handle_on_pos)
        
    def handle_on_pos(self, instance, value):
        self._bg_rectangle.pos = self.pos
        x = self.pos[0] + self.size[0]  / 4
        y = self.pos[1] + self.size[1] / 4
        self._fg_rectangle.pos = (x, y)
        

    def handle_on_size(self, instance, value):
        self._is_visible = True
        # background
        self._bg_rectangle.size = value
        # foreground
        w = value[0] / 2
        h = value[1] / 2
        self._fg_rectangle.size = (w, h)
        self._draw()

    def on_bg_color(self, instance, value):
        self._set_before()
        if self._is_visible and not self._is_drawing:
            self._draw()

    def on_fg_color(self, instance, value):
        if self._is_visible and not self._is_drawing:
            self._draw()
    
    # private methods

    def _set_before(self):
        before = self.canvas.before
        before.clear()
        before.add(Color(*self.bg_color))
        before.add(self._bg_rectangle)
        self.canvas.ask_update()

    def _draw(self):
        if not self._is_drawing:
            self._is_drawing = True
            canvas = self.canvas
            canvas.clear()
            canvas.add(Color(*self.fg_color))
            canvas.add(self._fg_rectangle)
            canvas.ask_update()
            self._is_drawing = False

        
class BGFGColorSelector(BoxLayout):

    display_size_hint = (None, 1.0)
    display_size = (dp(100), 1.0)
    
    def __init__(self, **kwargs):
        BoxLayout.__init__(self, **kwargs)
        self.orientation = 'horizontal'
        # left side
        self._display = _ColorDisplay(size_hint=BGFGColorSelector.display_size_hint,
                                      size=BGFGColorSelector.display_size)
        self.add_widget(self._display)
        # right side
        right_layout = BoxLayout(orientation='vertical',
                                 spacing=40)
        # background color adjustments
        slider_layout = GridLayout(cols=1)
        label = Label(text='Background')
        label.texture_update()
        label_ht = label.texture_size[1]
        label.size_hint_y = None
        label.height = label_ht
        slider_layout.add_widget(label)
        self._bg_slider = _ColorSliders(size_hint=(1.0, 1.0))
        self._bg_slider.bind(rgba_color=self.w_handle_bg_slider_value)
        slider_layout.add_widget(self._bg_slider)
        right_layout.add_widget(slider_layout)
        # fore ground
        slider_layout = GridLayout(cols=1)
        label = Label(text='Foreground',
                      size_hint_y=None,
                      height=label_ht)
        slider_layout.add_widget(label)
        self._fg_slider = _ColorSliders(size_hint=(1.0, 1.0))
        self._fg_slider.bind(rgba_color=self.w_handle_fg_slider_value)
        slider_layout.add_widget(self._fg_slider)
        right_layout.add_widget(slider_layout)
        self.add_widget(right_layout)
        
    # widget methods

    def w_handle_bg_slider_value(self, instance, value):
        self._display.bg_color = list(value)

    def w_handle_fg_slider_value(self, instance, value):
        self._display.fg_color = list(value)

    # parent methods

    @property
    def bg_color(self):
        c = self._bg_slider.rgba_color 
        return list(self._bg_slider.rgba_color)

    @bg_color.setter
    def bg_color(self, value):
        self._bg_slider.color = value

    @property
    def fg_color(self):
        c = self._fg_slider.rgba_color 
        return list(self._fg_slider.rgba_color)

    @fg_color.setter
    def fg_color(self, value):
        self._fg_slider.color = value